﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FCS.Test
{
    /// <summary>
    /// Summary description for CommonListOpTest
    /// </summary>
    [TestClass]
    public class CommonListOpTest : Functional
    {
        public CommonListOpTest()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        [TestMethod]
        public void HeadTest()
        {
            Assert.AreEqual(1, HeadValue(IntList(1)));
            Assert.AreEqual(1, Head(IntList(1)).Count());
            Assert.AreEqual(1, Length(Head(IntList(1))));

            Assert.AreEqual(0, Length(Head(IntList(1, -1))));
            Assert.AreEqual(0, Length(Head(Empty<int>())));
            
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void HeadEmptyTest()
        {
            Assert.AreEqual(null, HeadValue(Empty<int>()));
        }

        [TestMethod]
        public void TailTest()
        {
            Assert.AreEqual(2, HeadValue(Tail(IntList(1, 10))));
            Assert.AreEqual(9, Tail(IntList(1, 10)).Count());
            Assert.AreEqual(9, Length(Tail(IntList(1, 10))));

            Assert.AreEqual(0, Length(Tail(IntList(1, -1))));

            Assert.AreEqual(0, Length(Tail(Empty<int>())));
        }

        [TestMethod]
        public void ConsTest()
        {
            Assert.AreEqual(1, Length(Cons(1, null)));
            Assert.AreEqual(1, At(0, Cons(1, null)));

            Assert.AreEqual(3, Length(Cons(1, IntList(2,3))));
            Assert.AreEqual(1, At(0, Cons(1, IntList(2, 3))));
            Assert.AreEqual(2, At(1, Cons(1, IntList(2, 3))));
            Assert.AreEqual(3, At(2, Cons(1, IntList(2, 3))));

            Assert.AreEqual(2, Length(Cons(1, Cons(2, null))));
            Assert.AreEqual(2, At(1, Cons(1, Cons(2, null))));

            Assert.IsTrue(Equals(IntList(1, 10), Cons(1, IntList(2, 10))));

            var l = IntList(1, 10);
            Assert.IsTrue(Equals(l, Cons(HeadValue(l), Tail(l))));
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void ConsAfterEndTest()
        {
            At(2, Cons(1, Cons(2, null)));
        }

        [TestMethod]
        public void ConsLTest()
        {
            var f = IntList(2, 4);
            var t = IntList(1, 3);

            Assert.AreEqual(3, Length(ConsL(f, t)));

            Assert.IsTrue(Equals(ListFromArray(2, 1, 2, 3), At(0, ConsL(f, t))));
        }

        [TestMethod]
        public void ToListTest()
        {
            Assert.AreEqual(1, Length(ToList(1)));
            Assert.AreEqual(1, HeadValue(ToList(1)));
        }

        [TestMethod]
        public void ComposeTest()
        {
            Assert.AreEqual(1, Length(Compose(IntList(1, 1), (IFunList<int>)null)));
            Assert.AreEqual(1, At(0, Compose(IntList(1, 1), (IFunList<int>)null)));

            Assert.AreEqual(2, Length(Compose(IntList(1, 1), 1)));
            Assert.AreEqual(2, At(1, Compose(IntList(1, 1), 2)));

            Assert.AreEqual(4, Length(Compose(IntList(1, 2), IntList(2, 3))));
            Assert.AreEqual(1, At(0, Compose(IntList(1, 2), IntList(2, 3))));
            Assert.AreEqual(2, At(1, Compose(IntList(1, 2), IntList(2, 3))));
            Assert.AreEqual(2, At(2, Compose(IntList(1, 2), IntList(2, 3))));
            Assert.AreEqual(3, At(3, Compose(IntList(1, 2), IntList(2, 3))));

            Assert.AreEqual(8, Length(Compose(IntList(10, 15), IntList(1, 2))));
            Assert.AreEqual(1, At(0, Compose<int>(null, IntList(1, 2))));

            Assert.AreEqual(2, Length(Compose(IntList(1,1), Compose(IntList(2,2), Empty<int>()))));
            Assert.AreEqual(2, Length(Compose(IntList(1, 1), Compose(IntList(2, 2), Empty<int>()))));
            Assert.AreEqual(2, At(1, Compose(IntList(1, 1), Compose(IntList(2, 2), Empty<int>()))));
            //Assert.AreEqual(null, At(2, Compose(IntList(1, 1), Compose(IntList(2, 2), Empty<int>()))));

            Assert.IsTrue(Equals(IntList(1, 10), Compose(IntList(1,5), IntList(6, 10))));

            var l = IntList(1, 10);
            Assert.IsTrue(Equals(l, Compose(Head(l), Tail(l))));
        }

        [TestMethod]
        public void TakeTest()
        {
            Assert.AreEqual(5, Length(Take(5, IntList(1, 10))));
            Assert.AreEqual(4, Length(Take(5, IntList(1, 4))));
            Assert.AreEqual(4, Length(Take(1000, IntList(1, 4))));
            Assert.AreEqual(0, Length(Take(5, Empty<int>())));

            Assert.AreEqual(0, Length(Take(0, IntList(1, 4))));
            Assert.AreEqual(0, Length(Take(0, Empty<int>())));

            Assert.AreEqual(0, Length(Take(-1, IntList(1, 4))));
            Assert.AreEqual(0, Length(Take(-1, Empty<int>())));

            Assert.IsTrue(Equals(IntList(1, 8), Take(8, IntList(1, 10))));
            Assert.IsTrue(Equals(IntList(1, 8), Take(8, Compose(IntList(1, 5), IntList(6, 10)))));
        }
    }
}
